module Wondercap

module OuterConfigurationMixin
  include CompositeMultiLevelLogging
  
  def self.included(base)
    base.send :alias_method, :connection_factory_without_ocm, :connection_factory
    base.send :alias_method, :connection_factory, :connection_factory_with_ocm
  end
  
  class ConnectionFactoryWithCaching
    def initialize(wrapped_factory)
      @wrapped_factory = wrapped_factory
      @cached_sessions = {}
      @lock = Mutex.new
    end
    
    def connect_to(server)
      session = nil
      @lock.synchronize { session = @cached_sessions[server] }
      if session.nil?
        session = @wrapped_factory.connect_to(server)
        @lock.synchronize { @cached_sessions[server] = session }
      end
      session
    end
  end
  
  def connection_factory_with_ocm
    @connection_factory ||= ConnectionFactoryWithCaching.new(connection_factory_without_ocm)
  end
  
  def mc
    unless @mc
      @mc = Wondercap::MultiConfiguration.new
      @mc.extension_modules << Wondercap::InnerConfigurationMixin
      
      outer_conf = self
      @mc.initializers << lambda { |name,conf|        
        conf.set :outer_configuration, outer_conf
        conf.set :configuration_name, name
        conf.set :_defining_blocks, []
        
        conf.extend AnotherLoggerEchoedLogging
        def conf.outer_logger
          outer_configuration.logger
        end
        conf.logger.line_prefix = name.to_s

        conf.act_as :base if outer_conf.configuration_defined? :base
      }      
    end
    @mc
  end
  private :mc
  
  def configuration(name, &block)
    mc.add_configuration_defining_block(name, block)
    nil
  end
  
  def configuration_clone(name)
    conf = mc.create_orphaned_configuration(name)
    conf.act_as name
    conf.act_as :post_base if configuration_defined? :post_base
    conf
  end
  alias cclone configuration_clone
  
  def configuration_defined?(name)
    mc.is_defined? name
  end
  alias cdefined? configuration_defined?
  
  def configurations_names
    mc.names
  end
  alias cnames configurations_names
  
  def name_filter(name, &block)
    name_filters[name] = block
  end
  
  def extension(name_hash, &block)
    raise "name hash with single value expected" unless name_hash.respond_to? :each_pair
    raise "extension's name hash should have only one value" unless name_hash.size == 1
    
    extensions[name_hash.shift] = [block, {}]
  end
  
  def make_acting_as(conf, included_conf_name, options)
    blocks = mc.find_configuration_defining_blocks(included_conf_name)
    raise "can't act as non-existing configuration: #{included_conf_name}" unless blocks
    
    options.each { |k,v| conf.set k, v }
    blocks.each { |block| conf.instance_eval(&block) }
  end
  
  def get_name_filter(name)
    raise NameError.new("no filter #{name} defined") unless name_filters.key? name.to_sym
    name_filters[name.to_sym]
  end
  
  def get_substrategy_extension(substrategy, extension)
    key = [substrategy.to_sym, extension.to_sym]
    raise NameError.new("no extension #{extension} for substrategy #{substrategy} defined") unless extensions.include? key
    
    extensions[key]
  end
  
  def has_substrategy_extension?(substrategy, extension)
    extensions.key? [substrategy.to_sym, extension.to_sym]
  end
  
  private
  def extensions
    @extensions ||= Hash.new
  end
  
  def name_filters
    @filters ||= Hash.new
  end
  
end
      
end